# -*- encoding=utf8 -*-
import csv
import os
import time
import traceback
import subprocess
import xml.dom.minidom

from datetime import datetime, timedelta

import pymysql
from django.shortcuts import render

from apps.a4api.controler import auth_A4_api
from apps.assetmgnt.controler import reboot, acquire_devsoft_version, change_syslog_file, shutdown
from apps.assetmgnt.models import Device, DevType, DeviceSnmpConfig
from apps.auditdb.controler import auth_a4_use
from apps.firewall.controler import auth_t3_use
from apps.firewall.controler import send_socket, analysis_xml
from apps.firewall3_1.controler import auth_t3_v3_use
from apps.firewallV2.controler import auth_t3_v2_use
from apps.new_a4.controler import new_a4_login
from apps.log_audit.controler import log_audit_login
from apps.ids.controler import token_valid, IdsRequests
from apps.system.models import SysSetting
from apps.whitel.controler import audit_whl_token, auth_white_use
from safepf.settings_base import PASSWORD_KEY, SYSPATH
from utils.cryption import Cryption
from utils.decorators import service_required
from utils.viewdict import importance, importance_new
from utils.viewutil import get_user, ajaxresponse, pages, savelog
from apps.core.templatetags.audittags import get_fortree_token, leftdays
from safepf import settings
from apps.realtimeinfo.views import get_device_info


def get_all_dtype():
    devtypes = DevType.objects.values_list('id', 'name').order_by('id')
    dtypes = {}
    for dtype in devtypes:
        dtypes[dtype[0]] = dtype[1]
    return dtypes


def check_authenticate(request):
    """鉴权，校验设备是否允许使用"""
    curip = request.GET.get('ip', '').strip()
    devtype = int(request.GET.get('dtype', 2))   # 之前使用number来判断型号
    # 以下是根据ip获取 设备类型的型号
    devtypid = Device.objects.get(ip=curip).dtype_id
    devename = DevType.objects.get(id=devtypid).ename
    try:
        allauth = SysSetting.objects.get(key_str='IsAuthenticate').value_str
        if allauth == '0':
            allow = True
        else:
            dev = Device.objects.get(ip=curip)
            if dev.isauthenticate == 0:
                allow = True
            else:
                if devename == "A4":
                    allow = auth_a4_use(curip, dev.username, dev.passwd)
                elif devename == "A4Api":
                    pwd = bytes.decode(Cryption.decrypt(dev.passwd))
                    pwd = pwd.split(',')
                    allow = auth_A4_api(curip, {'user': dev.username, 'pswd': pwd[0]})
                elif devename == "T3-2.0" or devename == "T3-3.0":
                    pwd = Cryption.decrypt(dev.passwd).decode('utf-8').split(',')[0]
                    allow = auth_t3_v2_use(curip, {"method": "login", "params": [dev.username, pwd]})
                elif devename == "T3-3.1":
                    pwd = Cryption.decrypt(dev.passwd).decode('utf-8').split(',')[0]
                    allow = auth_t3_v3_use(curip, {"method": "login", "params": [dev.username, pwd]})
                elif devename == "A4-3.0":
                    # 新审计3.0api的验证
                    # pwd = Cryption.encrypt(pwd + ',' + PASSWORD_KEY)
                    dev = Device.objects.get(ip=curip)
                    username = dev.username
                    pwd = dev.passwd
                    realpwd = bytes.decode(Cryption.decrypt(pwd))
                    realpwd = realpwd.split(",")
                    allow = check_dev_can_use(curip, devename, username, realpwd[0])
                elif devename == "IDS":
                    allow = token_valid(curip)
                else:
                    pwd = bytes.decode(Cryption.decrypt(dev.passwd))
                    pwd = pwd.split(',')
                    allow = auth_t3_use(curip, dev.username, pwd[0])
        if allow:
            return ajaxresponse({'head': 'OK', 'body': u'设备允许使用'})
        else:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备不允许使用'})
    except Exception:
        return ajaxresponse({'head': 'ERROR', 'body': u'设备不允许使用'})


@service_required('sys_config')
def list_device(request):
    """设备列表"""
    devs = Device.objects.all()
    devicename = request.GET.get('devicename', '').strip()
    mac = request.GET.get('mac', '').strip()
    dip = request.GET.get('dip', '').strip()

    if devicename:
        devs = devs.filter(name__contains=devicename)
    if mac:
        devs = devs.filter(mac__icontains=mac)

    if dip:
        devs = devs.filter(ip__icontains=dip)
    s, e, pagination = pages(request, devs)

    return render(request, 'listasset.html', {
        'objs': devs[s:e], 'pagination': pagination, 'devicename': devicename, 'mac': mac, 'dip': dip
    })


@service_required('sys_config')
def add_device(request):
    """添加设备"""
    if request.POST:
        user = get_user(request)
        dev = Device()
        dev.name = request.POST.get('name', '').strip()
        dev.dtype_id = int(request.POST.get('dtype', 0))
        try:
            if Device.objects.get(name=dev.name):
                return ajaxresponse({'head': 'ERROR', 'body': u'添加设备失败，此设备名称已存在'})
        except Exception:
            pass
        dev.model = request.POST.get('model', '').strip()
        dev.mac = request.POST.get('mac', '').strip()
        dev.ip = request.POST.get('ip', '').strip()
        dev.endtime = request.POST.get('endtime', '')
        dev.devnumber = request.POST.get('devnumber', '').strip()
        dev.vendor = request.POST.get('vendor', '').strip()
        dev.importance = int(request.POST.get('importance', 0))
        dev.location = request.POST.get('location', '')
        dev.safeowner = request.POST.get('safeowner', '')
        url = request.POST.get('url', '')
        # isauth = int(request.POST.get('isauthenticate', 0))
        isauth = 1
        username = request.POST.get('username', '').strip()
        password = request.POST.get('passwd', '').strip()
        dev_token = request.POST.get('dev_token', '')
        dtyp = DevType.objects.get(id=dev.dtype_id)
        dtypid = dtyp.ename
        # if isauth == 0:
        #     if username != '':
        #         dev.username = ''
        #     if password != '':
        #         dev.passwd = ''
        if len(url):
            dev.desc = url
        if dtypid == 'IDS':
            dev.username = dev_token
            dev.passwd = ''
            res = check_dev_can_use(curip=dev.ip, dtype=dtypid, username=dev_token, pwd='')
            if not res:
                return ajaxresponse({'head': 'ERROR', 'body': u'设备鉴权失败,请输入有效的验证信息'})
        elif dtypid == "fortress_machine_V2":
            dev.username = username
            dev.passwd = ''
            res = get_fortree_token(dev.ip, username)
            # res = get_fortree_token("39.107.115.155:21043", username)
            if not res:
                return ajaxresponse({'head': 'ERROR', 'body': u'设备鉴权失败,请输入有效的验证信息'})
        elif dtypid[0:2] not in ("T3", 'A4', 'T9') and dtypid != "white-net" and dtypid != "LAS":
            dev.username = ''
            dev.passwd = ''
            isauth = 0
        else:
            dev.username = username
            dev.passwd = Cryption.encrypt(password + ',' + PASSWORD_KEY)
            res = check_dev_can_use(dev.ip, dtypid, username, password)
            if not res:
                return ajaxresponse({'head': 'ERROR', 'body': u'设备鉴权失败,请输入有效的验证信息'})
        devvision, softvision, sn = acquire_devsoft_version(dev.ip, dtypid, dev.hardnumber, _args=dev_token)
        dev.assert_no = sn
        dev.hardnumber = devvision
        dev.version = softvision
        dev.isauthenticate = isauth
        dev.save()
        subprocess.run('/usr/bin/python3.6 /usr/insec/safept/utils/eventmessage.py restart > /dev/console 2>&1',
                       shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        change_syslog_file()
        savelog(request, 'config', u'系统', u'系统', 6, u'设备', u'%s添加设备，设备名称为：%s' % (user, dev.name))
        return ajaxresponse({'head': 'OK', 'body': u'设备添加成功', 'redirect': '/assetmgnt/listdev'})
    else:
        devtypes = get_all_dtype()
        if not settings.IS_INNER:
            num = Device.objects.count()
            license = os.path.exists('/etc/sys_license.dat')
            if not license:
                return render(request, 'inform.html', {'title':u'系统错误', 'message':u'无授权文件，请先安装或更新授权文件'})
            try:
                os.system('decrypt_license /etc/sys_license.dat > %sstaticmedia/storage/tmplicense.xml' % SYSPATH)
                DomTree = xml.dom.minidom.parse('staticmedia/storage/tmplicense.xml')
                collection = DomTree.documentElement
                canuse = collection.getElementsByTagName('userdays')[0].childNodes[0].data
                start = collection.getElementsByTagName('time')[0].childNodes[0].data
                os.remove('%sstaticmedia/storage/tmplicense.xml' % SYSPATH)
                if canuse != 'a':
                    d = int(canuse)
                    last = datetime.strptime(start, '%Y%m%d') + timedelta(days=d)
                    if last > datetime.now():
                        if d == 365 and num > 10:
                            return render(request, 'inform.html', {'title': u'错误', 'message': u'被管设备已达上限'})
                        if d < 365 and num > 50:
                            return render(request, 'inform.html', {'title': u'错误', 'message': u'被管设备已达上限'})
                    else:
                        return render(request, 'inform.html', {'title': u'错误', 'message': u'授权文件已过期'})
            except Exception:
                import traceback
                print(traceback.format_exc())
                return render(request, 'inform.html', {'title': u'错误', 'message': u'授权文件错误'})

        return render(request, 'addasset.html', {'devtypes': devtypes})


@service_required('sys_config')
def edit_device(request):
    """修改设备"""
    if request.POST:
        user = get_user(request)
        id = request.POST.get('id', 0).strip()
        try:
            dev = Device.objects.get(id=id)
            dev.name = request.POST.get('name', '').strip()
            try:
                res = Device.objects.filter(name=dev.name)
                if len(res) == 0:
                    pass
                elif len(res) == 1:
                    if res[0].id == int(id):
                        pass
                    else:
                        return ajaxresponse({'head': 'ERROR', 'body': u'修改设备失败，此设备名称已存在'})
                else:
                    return ajaxresponse({'head': 'ERROR', 'body': u'修改设备失败，此设备名称已存在'})
            except Exception:
                pass
            dev.dtype_id = int(request.POST.get('dtype', 0))
            dev.desc = request.POST.get('desc', '').strip()
            dev.model = request.POST.get('model', '').strip()
            dev.mac = request.POST.get('mac', '').strip()
            dev.ip = request.POST.get('ip', '').strip()
            dev.endtime = request.POST.get('endtime', '')
            dev.devnumber = request.POST.get('devnumber', '').strip()
            dev.vendor = request.POST.get('vendor', '').strip()
            dev.importance = int(request.POST.get('importance', 0))
            dev.location = request.POST.get('location', '')
            dev.safeowner = request.POST.get('safeowner', '')
            # isauth = int(request.POST.get('isauthenticate', 0))
            isauth = 1
            dev.username = request.POST.get('username', '').strip()
            password = request.POST.get('passwd', '').strip()
            dev_token = request.POST.get('dev_token', '')
            url = request.POST.get('url', '')
            if len(url):
                dev.desc = url
            dtyp = DevType.objects.get(id=dev.dtype_id)
            dtypid = dtyp.ename

            if dtypid[0:2] != "T3" and dtypid[0:2] != "A4" and dtypid[0:2] != "T9" and dtypid != "white-net" and dtypid != 'IDS' and dtypid != 'LAS':
                isauth = 0
            dev.isauthenticate = isauth
            if password == '':
                dev.passwd = password
            else:
                dev.passwd = Cryption.encrypt(password + ',' + PASSWORD_KEY)
            if isauth:
                if dtypid == 'IDS':
                    dev.username = dev_token
                    res = check_dev_can_use(curip=dev.ip, dtype=dtypid, username=dev_token, pwd='')
                else:
                    res = check_dev_can_use(dev.ip, dtypid, dev.username, password)
                if not res:
                    return ajaxresponse({'head': 'ERROR', 'body': u'设备鉴权失败,输入有效的验证信息'})
            dev.save()
            subprocess.run(
                '/usr/bin/python3.6 /usr/insec/safept/utils/eventmessage.py restart > /dev/console 2>&1',
                shell=True,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )
            change_syslog_file()
            savelog(request, 'config', u'系统', u'系统', 6, u'设备', u'%s修改设备，被修改设备名称为：%s' % (user, dev.name))
        except Exception as e:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备信息修改失败:%s' % e})
        return ajaxresponse({'head': 'OK', 'body': u'设备信息编辑成功', 'redirect': '/assetmgnt/listdev'})
    else:
        did = request.GET.get('did', '').strip()
        id = Cryption.decrypt(did)
        devtypes = get_all_dtype()
        try:
            dev = Device.objects.get(id=id)
            password = dev.passwd
            if password == '':
                passwd = ''
            else:
                passwd1 = Cryption.decrypt(password).decode().split(',')
                passwd = passwd1[0]
            return render(request, 'editasset.html', {'dev': dev, 'devtypes': devtypes, 'passwd': passwd})
        except Exception:
            return render(request, 'inform.html', {'title': 'ERROR', 'message': u'无法获取设备'})


def check_dev_can_use(curip, dtype, username, pwd):
    """添加设备时校验用户名和密码是否正确"""
    allauth = SysSetting.objects.get(key_str='IsAuthenticate').value_str
    devtypenum = dtype
    if allauth == '0':
        return True
    if devtypenum == "T3-1.1":
        # 防火墙1.1的验证
        allow = auth_t3_use(curip, username, pwd)

    elif devtypenum == "T3-2.0" or devtypenum == "T3-3.0":
        # 防火墙2.0  3.0 的验证
        pamars = [username, pwd]
        data = {"method": "login", "params": pamars}
        allow = auth_t3_v2_use(curip, data=data)

    elif devtypenum == "T3-3.1" or devtypenum == "T3-5.0":
        # 防火墙3.1的验证
        pamars = [username, pwd]
        data = {"method": "login", "params": pamars}
        allow = auth_t3_v3_use(curip, data=data)

    elif devtypenum == "A4":
        # 审计的验证
        pwd = Cryption.encrypt(pwd + ',' + PASSWORD_KEY)
        allow = auth_a4_use(curip, username, pwd)
    elif devtypenum == "A4-3.0":
        # 新审计的验证
        data = {"username": username, "password": pwd}
        allow = new_a4_login(curip, data)
    elif devtypenum == "A4Api":
        # 审计api的验证
        # pwd = Cryption.encrypt(pwd + ',' + PASSWORD_KEY)
        data = {"user": username, "pswd": pwd}
        allow = auth_A4_api(curip, data)
    elif devtypenum == "white-net":
        """白名单网络版"""
        data = {"username": username, "password": pwd}
        allow = auth_white_use(curip, data=data)
    elif devtypenum == "IDS":
        allow = token_valid(curip, username, True)
    elif devtypenum == "LAS":
        # 日审
        data = {"username": username, "password": pwd}
        allow = log_audit_login(curip, data)
    else:
        pwd = Cryption.encrypt(pwd + ',' + PASSWORD_KEY)
        allow = auth_a4_use(curip, username, pwd)
    return allow


@service_required('sys_config')
def del_device(request):
    """删除设备"""
    hid = request.GET.get('did', '').strip()
    id = Cryption.decrypt(hid)
    user = get_user(request)
    try:
        dev = Device.objects.get(id=id)
        savelog(request, 'config', u'系统', u'系统', 6, u'设备', u'%s删除设备,被删设备名:%s' % (user.username, dev.name))
        dev.delete()
        subprocess.run('/usr/bin/python3.6 /usr/insec/safept/utils/eventmessage.py restart > /dev/console 2>&1',
                       shell=True,
                       stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        change_syslog_file()
    except Exception as e:
        return ajaxresponse({'head': 'ERROR', 'body': str(e)})
    return ajaxresponse({'head': 'OK', 'body': u'设备删除成功'})


@service_required('sys_config')
def reboot_dev(request):
    """重启设备"""
    did = request.GET.get('did', '').strip()
    id = Cryption.decrypt(did)
    user = get_user(request)
    try:
        cpu, mem, line, not_show = get_device_info(id)
        if not line:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备不在线无法重启'})
        dev = Device.objects.get(id=id)
        dtyename = DevType.objects.get(id=dev.dtype_id).ename
        res = reboot(dev.ip, dtyename)
        if res:
            savelog(request, 'config', u'系统', u'系统', 6, u'系统', u'%s重启了设备%s' % (user.username, dev.name))
            return ajaxresponse({'head': 'OK', 'body': u'设备重启中，请稍后'})
        else:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备重启失败'})
    except Exception as e:
        print(e)
        return ajaxresponse({'head': 'ERROR', 'body': u"设备重启失败"})


@service_required('sys_config')
def shutdown_dev(request):
    """停止设备"""
    did = request.GET.get('did', '').strip()
    id = Cryption.decrypt(did)
    user = get_user(request)
    try:
        cpu, mem, line, not_show = get_device_info(id)
        if not line:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备不在线无法停止'})
        dev = Device.objects.get(id=id)
        dtyename = DevType.objects.get(id=dev.dtype_id).ename
        res = shutdown(dev.ip, dtyename)
        if res:
            savelog(request, 'config', u'系统', u'系统', 6, u'系统', u'%s停止了设备%s' % (user.username, dev.name))
            return ajaxresponse({'head': 'OK', 'body': u'设备停止中，请稍后'})
        else:
            return ajaxresponse({'head': 'ERROR', 'body': u'设备停止失败'})
    except Exception as e:
        print(e)
        return ajaxresponse({'head': 'ERROR', 'body': u"设备停止失败"})


@service_required('sys_config')
def savedev(request):
    """保存配置"""
    ip = request.GET.get('ip')
    cmd = '<?xml version="1.0"?><CMD uri="set system save" from="root(/dev/pts/0@192.168.229.1:1909)"/>'
    cont = send_socket(ip, cmd)
    result = analysis_xml(cont)
    if 'WRONG' == result:
        return ajaxresponse({'head': 'ERROR', 'body': u'保存失败'})
    elif 'ERROR' == result:
        return ajaxresponse({'head': 'ERROR', 'body': u'保存失败'})
    else:
        return ajaxresponse({'head': 'OK', 'body': u'保存成功', 'redirect': '/assetmgnt/listdev/'})


@service_required('sys_config')
def snmp_config(request):
    """设备SNMP配置"""
    if request.POST:
        user = get_user(request)
        dev_id = request.POST.get('dev_id', 0).strip()
        try:
            dev_snmp_config = DeviceSnmpConfig.objects.filter(device_id=dev_id)
            ename = Device.objects.get(id=dev_id).dtype.ename
            config = {
                "device_id": dev_id,
                "protoctl": request.POST.get('protoctl', "TCP"),
                "version": request.POST.get('version'),
                "community": request.POST.get('community'),
                "flow_oid": request.POST.get('flow_oid'),
                "disk_oid": request.POST.get('disk_oid'),

            }
            map_msg = {
                "protoctl": "协议不能为空",
                "flow_oid": "流量oid不能为空",
                "disk_oid": "磁盘oid不能为空",
                "version": "版本号不能为空",
                "community": "团体号不能为空",
            }

            for k, v in config.items():
                if not v and k not in ["flow_oid", "disk_oid"] and ename not in ["toto_exchange", "turing_missed_sweeping"]:
                    return ajaxresponse({'head': 'ERROR', 'body': map_msg.get(k)})
            if dev_snmp_config.exists():
                dev_snmp_config.update(**config)
            else:
                DeviceSnmpConfig.objects.create(**config)

            savelog(request, 'config', u'系统', u'系统', 6, u'设备', u'%s更新设备SNMP配置' % (user))
        except Exception as e:
            import traceback
            print(traceback.format_exc())
            return ajaxresponse({'head': 'ERROR', 'body': u's更新设备SNMP配置失败:%s' % e})
        return ajaxresponse({'head': 'OK', 'body': u'更新设备SNMP配置成功', 'redirect': '/assetmgnt/listdev'})
    else:
        did = request.GET.get('did', '').strip()
        id = Cryption.decrypt(did)
        try:
            snmp_configs = DeviceSnmpConfig.objects.filter(device_id=id)
            ename = Device.objects.get(id=id).dtype.ename
            if snmp_configs.exists():
                snmp_config = snmp_configs.first()
            else:
                snmp_config = {
                    "protoctl": "TCP",
                    "version": "1",
                }
            dev = Device.objects.get(id=id)
            return render(request, 'update_device_snmp_config.html', {'snmp_config': snmp_config, "dev": dev, "ename": ename})
        except Exception:
            import traceback
            print(traceback.format_exc())
            return render(request, 'inform.html', {'title': 'ERROR', 'message': u'无法获取设备'})


@service_required('sys_config')
def dev_export(request):
    dev = Device.objects.filter().all()
    title = ["名称", "设备类型", "重要程度", "所处位置", "安全负责人", "产品型号", "MAC", "IP", "剩余维保天数", "鉴权用户名", "鉴权密码", "密码是否加密"]
    with open("staticmedia/storage/devlist.csv", "w", newline="", encoding='utf-8-sig') as f:
        writer = csv.writer(f)
        writer.writerow(title)
        for i in dev:
            dev_type = DevType.objects.get(id=i.dtype_id).name
            passwd = ""
            # if i.passwd:
                # passwd = Cryption.decrypt(i.passwd).decode('utf-8').split(',')[0]
            row = [i.name, dev_type, importance(i.importance), i.location, i.safeowner, i.devnumber, i.mac, i.ip,
                   leftdays(i.endtime),
                   i.username, i.passwd, "是"]
            writer.writerow(row)
    return ajaxresponse({'head': 'OK', 'body': '/media/storage/devlist.csv'})


@service_required('sys_config')
def dev_import(request):
    db = pymysql.connect("localhost", "insec", "InSec888!", "safeplatform")
    cursor = db.cursor()
    now = datetime.strftime(datetime.now(), '%Y-%m-%d')
    file = request.FILES.get("file_input")
    if '.csv' not in file.name:
        return ajaxresponse({'head': 'ERROR', 'body': u'文件格式不正确'})
    content = file.read().decode('utf-8')
    if content and "设备类型" in content:
        for item in content.split('\n'):
            if '设备类型' not in item:
                data = item.split(',')
                if len(data) > 1:
                    dev_name = Device.objects.filter(name=data[0]).all()
                    if not dev_name:
                        devtype_name = DevType.objects.filter(name=data[1]).all()
                        if data[10]:
                            if data[11].replace("\r", "") == "否":
                                passwd = Cryption.encrypt(data[10] + ',' + PASSWORD_KEY)
                            else:
                                passwd = data[10]
                        if data[8]:
                            future_date = datetime.now() + timedelta(days=int(data[8]))
                            # 格式化日期
                            end_time = future_date.strftime('%Y-%m-%d')
                        if devtype_name:
                            dev_name = DevType.objects.get(name=data[1])
                            sql = f"INSERT INTO `safeplatform`.`t_device` (`name`, `dtype_id`, `assert_no`, `desc`, `version`, `hardnumber`, `safeowner`, `mac`, `ip`, `online`, `addtime`, `endtime`, `isauthenticate`, `username`, `passwd`, `importance`, `location`, `devnumber`) VALUES ('{data[0]}', '{dev_name.id}', '', '', '', 'INS-T3358-FQA', '{data[4]}', '{data[6]}', '{data[7]}', 0, '{now}', '{end_time}', 1, '{data[9]}', '{passwd}', '{importance_new(data[2])}', '{data[3]}', '{data[5]}');"
                            cursor.execute(sql)
        db.commit()
        cursor.close()
        db.close()
    else:
        return ajaxresponse({'head': 'ERROR', 'body': u'文件内容不正确'})
    return ajaxresponse({'head': 'OK', 'body': u'导入成功'})
